package com.hmy.contract.service.contract.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hmy.ccp.common.response.BasePageResponse;
import com.hmy.ccp.datapermission.aop.DataPermission;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.ccp.starter.web.utils.UserContextUtil;
import com.hmy.contract.bo.request.contract.*;
import com.hmy.contract.bo.response.contract.*;
import com.hmy.contract.bo.response.order.OrderDeviceDetailResponse;
import com.hmy.contract.bo.response.order.SupplyOrderDetailResponse;
import com.hmy.contract.commons.enums.ContractStatusEnum;
import com.hmy.contract.commons.enums.ContractTypeEnum;
import com.hmy.contract.dal.mapper.SupplyContractMapper;
import com.hmy.contract.dal.po.ContractConfigPo;
import com.hmy.contract.dal.po.SupplyContractPo;
import com.hmy.contract.manager.order.SupplyOrderFeignClientManager;
import com.hmy.contract.service.contract.IContractConfigService;
import com.hmy.contract.service.contract.ISupplyContractApprovalService;
import com.hmy.contract.service.contract.ISupplyContractService;
import com.hmy.contract.service.contract.create.ContractCreateServiceFactory;
import com.hmy.contract.service.contract.create.IContractCreateService;
import com.hmy.contract.service.contract.save.supply.ISaveSupplyContractService;
import com.hmy.contract.service.contract.save.supply.SaveSupplyContractServiceFactory;
import com.hmy.contract.service.dto.contract.*;
import com.hmy.contract.service.dto.save.SaveSupplyContractDto;
import com.hmy.contract.service.rdto.ContractActorDetailRdto;
import com.hmy.contract.service.rdto.ContractConfigRdto;
import com.hmy.contract.service.rdto.CreateContractRdto;
import com.hmy.contract.service.rdto.SupplyContractRdto;
import com.hmy.order.api.bo.request.SupplyOrderListDetailQueryApiRequest;
import com.hmy.order.api.bo.response.SupplyOrderPrimaryApiResponse;
import com.hmy.order.api.common.enums.OrderDeviceEnum;
import com.hmy.workflow.api.bo.response.process.StartProcessInstanceResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author seven.qi 齐胜南
 * @version 1.0
 * @description
 * @date 2024/3/13 14:12
 */
@Service
@Slf4j
public class SupplyContractServiceImpl implements ISupplyContractService {

    @Resource
    private SupplyOrderFeignClientManager supplyOrderFeignClientManager;

    @Resource
    private ISupplyContractApprovalService supplyContractApprovalService;

    @Resource
    private SupplyContractMapper supplyContractMapper;

    @Resource
    private IContractConfigService contractConfigService;

    @Override
    public CreateSupplyContractResponse createSupplyContract(CreateSupplyContractRequest createSupplyContractRequest) {
        SupplyOrderListDetailQueryApiRequest request = new SupplyOrderListDetailQueryApiRequest();
        request.setSupplyOrderIds(List.of(createSupplyContractRequest.getSupplyOrderId()));
        List<SupplyOrderPrimaryApiResponse> supplyOrderDetailByIds = supplyOrderFeignClientManager.getSupplyOrderDetailByIds(request);
        if (CollUtil.isEmpty(supplyOrderDetailByIds) || supplyOrderDetailByIds.size() > 1) {
            log.info("SupplyContractService.createSupplyContract supplyOrderDetailByIds is empty or size > 1, {}", supplyOrderDetailByIds);
            throw new HMYRuntimeException(502000033);
        }

        // 从订单侧获取增补订单信息，转换成SaveSupplyContractDto
        SupplyOrderDetailDto supplyOrderDetailDto = BeanUtil.copyProperties(supplyOrderDetailByIds.get(0), SupplyOrderDetailDto.class);
        SaveSupplyContractDto dto = BeanUtil.copyProperties(createSupplyContractRequest, SaveSupplyContractDto.class);
        dto.setSupplyOrderDetail(supplyOrderDetailDto);

        ISaveSupplyContractService saveSupplyContractService = SaveSupplyContractServiceFactory.getInstance(dto.getType());

        saveSupplyContractService.beforeCreate(dto);

        SupplyContractPo contractPo = saveSupplyContractService.save(dto);

        saveSupplyContractService.afterCreate(dto);

        CreateSupplyContractResponse createSupplyContractResponse = new CreateSupplyContractResponse();
        createSupplyContractResponse.setSupplyContractId(contractPo.getSupplyContractId());
        return createSupplyContractResponse;
    }

    @Override
    public SupplyContractInfoResponse getSupplyContractInfo(SupplyContractInfoRequest supplyContractInfoRequest) {
        SupplyContractPo supplyContract =
                supplyContractMapper.selectOne(Wrappers.lambdaQuery(SupplyContractPo.class)
                        .eq(SupplyContractPo::getIsDeleted, false)
                        .eq(SupplyContractPo::getSupplyContractId, supplyContractInfoRequest.getSupplyContractId()));
        SupplyContractInfoResponse response = new SupplyContractInfoResponse();
        if (supplyContract != null) {
            SupplyOrderListDetailQueryApiRequest request = new SupplyOrderListDetailQueryApiRequest();
            request.setSupplyOrderIds(List.of(supplyContract.getSupplyOrderId()));
            List<SupplyOrderPrimaryApiResponse> supplyOrderDetailByIds = supplyOrderFeignClientManager.getSupplyOrderDetailByIds(request);
            if (CollUtil.isEmpty(supplyOrderDetailByIds) || supplyOrderDetailByIds.size() > 1) {
                log.info("SupplyContractService.createSupplyContract supplyOrderDetailByIds is empty or size > 1, {}", supplyOrderDetailByIds);
                throw new HMYRuntimeException(502000033);
            }
            response = BeanUtil.copyProperties(supplyContract, SupplyContractInfoResponse.class, "customerExtension");
            SupplyOrderDetailResponse supplyOrderDetail = JSONUtil.toBean(supplyContract.getOrderDetailExtension(),
                    SupplyOrderDetailResponse.class);
            if (!Objects.isNull(supplyOrderDetailByIds.get(0)) && CollUtil.isNotEmpty(supplyOrderDetailByIds.get(0).getAllOrderDeviceDetails())) {
                supplyOrderDetail.setAllOrderDeviceDetails(BeanUtil.copyToList(supplyOrderDetailByIds.get(0).getAllOrderDeviceDetails(), OrderDeviceDetailResponse.class));
            }
            Map<String, OrderDeviceDetailResponse> detailPoMap = Maps.newHashMap();
            if (!CollectionUtils.isEmpty(supplyOrderDetail.getOrderDeviceDetails())) {
                supplyOrderDetail.getOrderDeviceDetails().forEach(xx -> {
                    xx.setDeviceTypeAlias(OrderDeviceEnum.getDescByType(xx.getDeviceType()));
                    String key = buildKey(xx.getDeviceModel(), xx.getDeviceType(), xx.getDeviceHeight());
                    detailPoMap.put(key, xx);
                });
            }
            if (!CollectionUtils.isEmpty(supplyOrderDetail.getSupplyOrderDeviceDetails())) {
                supplyOrderDetail.getSupplyOrderDeviceDetails().forEach(yy -> {
                    //  "extendedFields": "{\"deviceHeight\": \"8\", \"deviceTypeAlias\": \"剪叉车\"}",
                    Map map = JSONUtil.toBean(yy.getExtendedFields(), Map.class);
                    yy.setDeviceHeight(map.get("deviceHeight") == null ? null : map.get("deviceHeight").toString());
                    yy.setDeviceTypeAlias(map.get("deviceTypeAlias") == null ? null :
                            map.get("deviceTypeAlias").toString());
                    String key = buildKey(yy.getDeviceModel(), yy.getDeviceType(), yy.getDeviceHeight());
                    OrderDeviceDetailResponse orderDeviceDetail = detailPoMap.get(key);
                    // 判断日租单价 月租单价是否发生变动
                    if (Objects.nonNull(orderDeviceDetail)) {
                        yy.setDailyModifyPrice(yy.getDailyRent().compareTo(orderDeviceDetail.getDailyRent()));
                        yy.setMonthlyModifyPrice(yy.getMonthlyRent().compareTo(orderDeviceDetail.getMonthlyRent()));
                    }
                });
            }
            response.setSupplyOrderDetail(supplyOrderDetail);
            response.setContractTypeDesc(ContractTypeEnum.getDescByType(supplyContract.getContractType()));
            response.setStatusDesc(ContractStatusEnum.getDescByStatus(supplyContract.getStatus()));
            response.setProcessInstanceId(supplyContract.getProcessId());
            response.setLessorDetail(JSONUtil.toBean(supplyContract.getLessorInfo(),
                    LessorInfoResponse.class));
            if (StringUtils.isNotBlank(supplyContract.getContractSignTaskUrl())) {
                SignTaskDto signTaskDto = JSONUtil.toBean(supplyContract.getContractSignTaskUrl(), SignTaskDto.class);
                response.setContractSignTaskUrl(signTaskDto.getContractSignTaskUrl());
            }
            response.setCustomerExtension(JSONUtil.toBean(supplyContract.getCustomerExtension(),
                    CustomerExtensionResponse.class));
        }
        return response;
    }

    @Override
    public UploadSupplyContractFileUrlResponse uploadSupplyContractFileUrl(UploadSupplyContractFileUrlRequest updateSupplyContractFileUrlRequest) {
        log.info("uploadSupplyContractFileUrl request {}", updateSupplyContractFileUrlRequest);
        SupplyContractPo supplyContractPo =
                querySupplyContractPo(updateSupplyContractFileUrlRequest.getSupplyContractId());
        if (supplyContractPo == null) {
            throw new HMYRuntimeException(502000035);
        }
        String processInstanceId = null;
        if (ContractStatusEnum.REJECT.getStatus().equals(supplyContractPo.getStatus())) {
            log.info("uploadSupplyContractFileUrl reSubmit");
            supplyContractApprovalService.reSubmit(supplyContractPo);
        } else {
            StartProcessInstanceResponse res = supplyContractApprovalService.startApprove(supplyContractPo);
            processInstanceId = res.getProcessInstanceId();
        }
        updateSupplyContractPo(supplyContractPo, updateSupplyContractFileUrlRequest, processInstanceId);

        UploadSupplyContractFileUrlResponse response = new UploadSupplyContractFileUrlResponse();
        response.setSupplyContractId(updateSupplyContractFileUrlRequest.getSupplyContractId());
        return response;
    }

    @Override
    @DataPermission(sql = "com.hmy.contract.dal.mapper.SupplyContractMapper.selectPage", userField =
            "sale_manager_id", deptField = "dept_id")
    public BasePageResponse<SupplyContractPageResponse> page(SupplyContractPageRequest supplyContractPageRequest) {
        Page<SupplyContractPo> page = new Page<>(supplyContractPageRequest.getPageNum(),
                supplyContractPageRequest.getPageSize());
        Page<SupplyContractPo> pageSource = supplyContractMapper.selectPage(page,
                setCondition(supplyContractPageRequest));
        List<SupplyContractPageResponse> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(pageSource.getRecords())) {
            pageSource.getRecords().forEach(SupplyContract -> {
                SupplyContractPageResponse res = new SupplyContractPageResponse();
                BeanUtil.copyProperties(SupplyContract, res, "customerExtension");
                res.setContractTypeDesc(ContractTypeEnum.getDescByType(SupplyContract.getContractType()));
                res.setStatusDesc(ContractStatusEnum.getDescByStatus(SupplyContract.getStatus()));
                res.setProcessInstanceId(SupplyContract.getProcessId());
                SupplyOrderDetailResponse supplyOrderDetail = JSONUtil.toBean(SupplyContract.getOrderDetailExtension(),
                        SupplyOrderDetailResponse.class);
                res.setSupplyOrderDetail(supplyOrderDetail);
                LessorInfoResponse lessorInfoDetail = JSONUtil.toBean(SupplyContract.getLessorInfo(),
                        LessorInfoResponse.class);
                res.setLessorDetail(lessorInfoDetail);
                CustomerExtensionResponse customerExtension = JSONUtil.toBean(SupplyContract.getCustomerExtension(),
                        CustomerExtensionResponse.class);
                res.setCustomerExtension(customerExtension);
                if (StringUtils.isNotBlank(SupplyContract.getContractSignTaskUrl())) {
                    SignTaskDto signTaskDto = JSONUtil.toBean(SupplyContract.getContractSignTaskUrl(),
                            SignTaskDto.class);
                    res.setContractSignTaskUrl(signTaskDto.getContractSignTaskUrl());
                }
                list.add(res);
            });
        }
        BasePageResponse<SupplyContractPageResponse> response = new BasePageResponse<>();
        response.setRecords(list);
        response.setTotal(pageSource.getTotal());
        response.setSize(supplyContractPageRequest.getPageSize());
        response.setCurrent(supplyContractPageRequest.getPageNum());
        response.setPages(pageSource.getPages());
        return response;
    }

    @Override
    public ModifySupplyContractModelResponse modifySupplyContractSignModel(ModifySupplyContractModelRequest modifySupplyContractModelRequest) {
        return null;
    }

    @Override
    public List<Long> getWaitingCreateSupplyContract(Long count) {
        LambdaQueryWrapper<SupplyContractPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SupplyContractPo::getStatus, ContractStatusEnum.WAITING_CREATE.getStatus());
        wrapper.eq(SupplyContractPo::getIsDeleted, 0);
        wrapper.orderByAsc(SupplyContractPo::getCreateTime);
        Page<SupplyContractPo> page = new Page<>(1, count);
        Page<SupplyContractPo> roPage = supplyContractMapper.selectPage(page, wrapper);
        if (roPage == null || CollUtil.isEmpty(roPage.getRecords())) {
            return null;
        }
        return roPage.getRecords().stream().map(SupplyContractPo::getSupplyContractId).collect(Collectors.toList());
    }

    @Override
    public void createSupplyContract(Long supplyContractId) {
        SupplyContractPo contractRo = supplyContractMapper.selectById(supplyContractId);
        if (ObjectUtil.isEmpty(contractRo)) {
            throw new HMYRuntimeException(502000035);
        }
        CreateContractRdto createContractRdto = getCreateContractRdto(contractRo);
        createContractRdto.setSupplyContractRdto(BeanUtil.copyProperties(contractRo, SupplyContractRdto.class));
        IContractCreateService contractCreateService = ContractCreateServiceFactory.getInstance(createContractRdto.getBusinessType());

        contractCreateService.beforeCreate(createContractRdto);
        // 电子合同的signTaskId是法大大的任务id，纸质合同是存储文件模板的id
        String signTaskId = contractCreateService.create(createContractRdto);
        createContractRdto.getSupplyContractRdto().setContractTaskId(signTaskId);
        // 电子合同的url是签署地址，纸质合同的url是文件存储到oss的相对路径
        String url = contractCreateService.start(signTaskId, contractCreateService.fillFieldValue(createContractRdto), createContractRdto);
        createContractRdto.setUrl(url);

        contractCreateService.afterCreate(createContractRdto);
    }

    @Override
    public void updateSupplyContractStatusFailed(Long supplyContractId) {
        SupplyContractPo contractPo = new SupplyContractPo();
        contractPo.setStatus(ContractStatusEnum.CONTRACT_CREATE_FAILED.getStatus());
        LambdaQueryWrapper<SupplyContractPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SupplyContractPo::getSupplyContractId, supplyContractId);
        wrapper.eq(SupplyContractPo::getIsDeleted, Boolean.FALSE);
        supplyContractMapper.update(contractPo, wrapper);
    }

    @Override
    public SupplyContractInfoListResponse supplyContractInfoList(SupplyContractInfoListRequest request) {
        log.info("SupplyContractServiceImpl supplyContractInfoList request:{}", JSON.toJSONString(request));
        SupplyContractInfoListResponse response = new SupplyContractInfoListResponse();
        List<SupplyContractPo> supplyContractPoList = supplyContractMapper.selectList(
                Wrappers.lambdaQuery(SupplyContractPo.class)
                        .in(CollUtil.isNotEmpty(request.getSupplyContractIdList()), SupplyContractPo::getSupplyContractId, request.getSupplyContractIdList())
                        .in(CollUtil.isNotEmpty(request.getSupplyContractNoList()), SupplyContractPo::getSupplyContractNo, request.getSupplyContractNoList())
                        .in(CollUtil.isNotEmpty(request.getSupplyOrderIdList()), SupplyContractPo::getSupplyOrderId, request.getSupplyOrderIdList())
                        .in(CollUtil.isNotEmpty(request.getSupplyOrderNoList()), SupplyContractPo::getSupplyOrderNo, request.getSupplyOrderNoList())
                        .eq(Objects.nonNull(request.getSupplyOrderId()), SupplyContractPo::getSupplyOrderId, request.getSupplyOrderId())
                        .eq(StringUtils.isNotBlank(request.getSupplyOrderNo()), SupplyContractPo::getSupplyContractNo, request.getSupplyOrderNo())
                        .eq(SupplyContractPo::getIsDeleted, 0)
        );

        if (CollUtil.isEmpty(supplyContractPoList)) {
            response.setSupplyContractInfoList(Lists.newArrayList());
            return response;
        }

        List<SupplyContractResponse> supplyContractInfoList = supplyContractPoList.stream().map(item -> {
            SupplyContractResponse supplyContractResponse = BeanUtil.copyProperties(item, SupplyContractResponse.class);
            supplyContractResponse.setContractTypeDesc(ContractTypeEnum.getDescByType(item.getContractType()));
            supplyContractResponse.setStatusDesc(ContractStatusEnum.getDescByStatus(item.getStatus()));
            
            return supplyContractResponse;
        }).collect(Collectors.toList());

        response.setSupplyContractInfoList(supplyContractInfoList);
        return response;
    }

    private CreateContractRdto getCreateContractRdto(SupplyContractPo contractRo) {
        LambdaQueryWrapper<ContractConfigPo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ContractConfigPo::getContractBusinessType, contractRo.getBusinessType());
        wrapper.eq(ContractConfigPo::getIsDeleted, Boolean.FALSE);
        ContractConfigPo contractConfigRo = contractConfigService.getOne(wrapper);
        if (ObjectUtil.isEmpty(contractConfigRo)) {
            throw new HMYRuntimeException(502000017);
        }

        ActorConfigDto config = JSONUtil.toBean(contractConfigRo.getActorConfig(), ActorConfigDto.class);
        ActorDetailExtensionDto actorDto = JSONUtil.toBean(contractRo.getActorDetailExtension(), ActorDetailExtensionDto.class);
        CreateContractRdto createContractRdto = new CreateContractRdto();
        createContractRdto.setFieldMap(JSONUtil.toBean(contractRo.getTemplateDataDetailExtension(), TemplateDataDetailExtensionDto.class).getFields());
        createContractRdto.setSignTemplateId(contractConfigRo.getTemplateId());
        createContractRdto.setContractActorDetailList(buildContractActorDetailList(actorDto.getActorDetailList(), config.getActorDetailList()));
        createContractRdto.setBusinessId(contractConfigRo.getBusinessId());
        createContractRdto.setTaskSubject(contractRo.getSupplyContractName());
        createContractRdto.setBusinessType(contractRo.getBusinessType());
        createContractRdto.setContractConfigRdto(BeanUtil.copyProperties(contractConfigRo, ContractConfigRdto.class, "templateFieldConfig"));
        return createContractRdto;
    }

    private List<ContractActorDetailRdto> buildContractActorDetailList(List<ContractActorDto> actorDto, List<ContractActorDetailRdto> config) {
        List<ContractActorDetailRdto> contractActorDetailRdtoList = new ArrayList<>();
        Map<String, List<ContractActorDetailRdto>> collect = config.stream().collect(Collectors.groupingBy(ContractActorDetailRdto::getActorId));
        actorDto.forEach(actor -> {
            ContractActorDetailRdto contractActorDetailRdto = collect.get(actor.getActorId()).get(0);
            contractActorDetailRdto.setActorName(actor.getActorName());
            contractActorDetailRdto.setNotifyAddress(actor.getActorPhone());
            contractActorDetailRdtoList.add(contractActorDetailRdto);
        });
        return contractActorDetailRdtoList;
    }

    /**
     * 设置查询条件
     *
     * @param request 请求对象
     * @return 查询条件
     */
    private LambdaQueryWrapper<SupplyContractPo> setCondition(SupplyContractPageRequest request) {
        LambdaQueryWrapper<SupplyContractPo> query = Wrappers.lambdaQuery();
        query.eq(SupplyContractPo::getIsDeleted, false);
        query.eq(request.getContractId() != null, SupplyContractPo::getContractId, request.getContractId());
        query.in(CollectionUtil.isNotEmpty(request.getStatus()), SupplyContractPo::getStatus, request.getStatus());
        query.eq(request.getContractType() != null, SupplyContractPo::getContractType, request.getContractType());
        if (StringUtils.isNotBlank(request.getSearchValue())) {
            query.and(wra -> wra.like(SupplyContractPo::getContractNo, request.getSearchValue())
                    .or().like(SupplyContractPo::getSupplyContractNo, request.getSearchValue())
                    .or().like(SupplyContractPo::getProjectName, request.getSearchValue())
                    .or().like(SupplyContractPo::getCustomerName, request.getSearchValue())
            );
        }
        query.orderByDesc(SupplyContractPo::getCreateTime);
        return query;
    }

    /**
     * 查询增补合同信息
     *
     * @param supplyContractId 增补合同id
     * @return 增补合同信息
     */
    private SupplyContractPo querySupplyContractPo(Long supplyContractId) {
        return supplyContractMapper.selectOne(Wrappers.lambdaQuery(SupplyContractPo.class)
                .eq(SupplyContractPo::getIsDeleted, false)
                .eq(SupplyContractPo::getSupplyContractId, supplyContractId));
    }

    /**
     * 更新合同
     *
     * @param supplyContractPo  增补合同信息
     * @param updateRequest     更新请求
     * @param processInstanceId 流程实例id
     */
    private void updateSupplyContractPo(SupplyContractPo supplyContractPo,
                                        UploadSupplyContractFileUrlRequest updateRequest, String processInstanceId) {
        SupplyContractPo supplyContract = new SupplyContractPo();
        supplyContract.setSupplyContractId(updateRequest.getSupplyContractId());
        UserContextUtil.initUpdaterInfo(supplyContract);
        supplyContract.setUpdateTime(LocalDateTime.now());
        supplyContract.setContractSignFileUrl(updateRequest.getFileUrl());
        supplyContract.setStatus(ContractStatusEnum.WAITING_APPROVAL.getStatus());
        supplyContract.setSignTime(LocalDateTime.now());
        supplyContract.setProcessId(processInstanceId);
        supplyContract.setContractFileName(supplyContractPo.getSupplyContractName());
        supplyContractMapper.updateById(supplyContract);
    }


    /**
     * 构建map中key
     *
     * @param deviceModel  设备型号
     * @param deviceType   设备类型
     * @param deviceHeight 设备高度
     * @return 拼接字符串
     */
    private static String buildKey(String deviceModel, String deviceType, String deviceHeight) {
        return deviceModel.concat("_").concat(deviceType).concat("_").concat(deviceHeight);
    }

}
